package com.dylan.channel.v9;

import com.dylan.channel.v9.attribute.AttributeKey;
import com.dylan.channel.v9.channel.Channel;
import com.dylan.channel.v9.channel.NioServerSocketChannel;
import com.dylan.channel.v9.future.ChannelFuture;
import com.dylan.channel.v9.future.ChannelPromise;
import com.dylan.channel.v9.future.DefaultChannelPromise;
import com.dylan.channel.v9.option.ChannelOption;
import com.dylan.util.ObjectUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Logger;

public class ServerBootstrap extends AbstractBootStrap<ServerBootstrap, NioServerSocketChannel> {
   private static final Logger logger = Logger.getLogger(ServerBootstrap.class.getName());
    volatile EventLoopGroup childGroup;
    // purposes.
    private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();

    public ServerBootstrap group(EventLoopGroup parent, EventLoopGroup childGroup) {
        this.group = parent;
        this.childGroup = childGroup;
        return this;
    }

    public ChannelFuture bind(int port) throws IOException {
        return this.bind(new InetSocketAddress(port));
    }

    private ChannelFuture bind(InetSocketAddress address) throws IOException {
        return this.doBind(address);
    }

    private ChannelFuture doBind(InetSocketAddress address) throws IOException {
        ChannelFuture regFuture = initAndRegister();
        Channel channel = regFuture.channel();

        //TODO构建Promise
        ChannelPromise promise = new DefaultChannelPromise(channel);

        this.doBind0(regFuture, channel, address, promise);

        return promise;
    }

    private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
        channel.eventLoop().execute(new Runnable() {
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise);
                    promise.setSuccess();
                }
            }
        });
    }

    void init(Channel channel) {
        //得到所有存储在map中的用户设定的channel的参数
        final Map<ChannelOption<?>, Object> options = options0();

        synchronized (options) {
            //把初始化时用户配置的参数全都放到channel的config类中，因为没有引入netty源码的打印日志模块，
            //所以就把该方法修改了，去掉了日志参数
            setChannelOptions(channel, options);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Map.Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
    }

    static void setChannelOptions(Channel channel, Map<ChannelOption<?>, Object> options) {
        for (Map.Entry<ChannelOption<?>, Object> e : options.entrySet()) {
            setChannelOption(channel, e.getKey(), e.getValue());
        }
    }


    @SuppressWarnings("unchecked")
    private static void setChannelOption(Channel channel, ChannelOption<?> option, Object value) {
        try {
            if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
                logger.warning("Unknown channel option '{" + option + "}' for channel '{" + channel + "}'");
            }
        } catch (Throwable t) {
            logger.warning("Failed to set channel option '{"+option+"}' with value '{"+value+"}' for channel '{"+channel+"}'");
        }
    }

    public <T> ServerBootstrap childOption(ChannelOption<T> option, T value) {
        ObjectUtil.checkNotNull(option, "text");
        synchronized (childOptions) {
            if (value == null) {
                childOptions.remove(option);
            } else {
                childOptions.put(option, value);
            }
        }
        return this;
    }
}
